import { Boid } from "./Boid";
import { Point, Vector } from "./Vector";

type functionType = "linear" | "log" | "exp";
/**
 * 信息素，用于吸引或者驱散鸟群
 * Emitters emit a radial force which can either attract or repel
 * boids. Emitters have various function types (linear, log, exp)
 * and can stack with each other.
 */
class Emitter {
    public position: Vector;
    updatePosition: (...args: any[]) => Point;
    /**
     *
     * @param updatePosition 定义如何更新坐标
     * @param strength 吸引或排斥的力度
     * @param radius 效果半径
     * @param functionType 效果类型
     * @param repel 是排斥吗
     */
    constructor(
        updatePosition: (...args: any[]) => Point,
        public strength: number,
        public radius: number,
        public functionType: functionType,
        public repel: boolean
    ) {
        this.updatePosition = updatePosition;
        const pos = updatePosition();
        this.position = new Vector(pos.x, pos.y);
    }
    moveTo(x: number, y: number) {
        this.position = new Vector(x, y);
    }
    update(flock: Boid[]) {
        //Affect any boids within range.
        flock.forEach((boid) => {
            const distance = boid.position.getDistance(this.position);

            if (distance <= this.radius) {
                var subAmount = 0;

                switch (this.functionType) {
                    case "linear":
                        subAmount = Math.min(this.strength, distance);
                        break;
                    case "log":
                        subAmount = Math.max(0, Math.log(distance));
                        break;

                    case "exp":
                        subAmount = Math.max(0, distance * distance);
                        break;
                }

                var influenceStrength = this.strength - subAmount;

                if (influenceStrength < 0) influenceStrength = 0;

                let influence: Vector;
                //Add to the boid's current tick.
                if (this.repel)
                    influence = this.position
                        .getDirectionTo(boid.position)
                        .scale(influenceStrength);
                else
                    influence = boid.position
                        .getDirectionTo(this.position)
                        .scale(influenceStrength);

                boid.emitterInfluences.push(influence);
            }
        });
    }
}

export { Emitter };
